/*!
 * pjax(ajax + history.pushState) for jquery
 * 
 * by welefen
 */
(function($) {
    $.pjax = function(opts) {
        opts = $.extend({
            selector: '',
            container: '',
            callback: function() {
            },
            initUI: function() {
            }
        }, opts);

        opts.initUI && opts.initUI();

        if (!opts.container || !opts.selector) {
            throw new Error('selector & container opts must be set');
        }

        $('body').on('click', opts.selector, function(event) {
            if (event.which > 1 || event.metaKey)
                return true;
            var $this = $(this), href = $this.attr('href');
            if (this.tagName.toUpperCase() !== 'A') {
                throw new Error('click requires an anchor element');
            }
            if (href === location.href)
                return true;
            opts.url = requestURL(href);
            //console.log(opts.url+"--"+requestURL(location.href));
            if (opts.url === requestURL(location.href))
                return true;
            event.preventDefault();
            opts = $.extend(true, opts, {
                _url: href,
                element: this
            });
            // 发起请求
            $.pjax.request(opts);
        });

        $('body').on('submit', opts.selector, function(event) {
            if (event.which > 1 || event.metaKey)
                return true;
            var $this = $(this), href = $this.attr('action');
            if (this.tagName.toUpperCase() !== 'FORM') {
                throw new Error('submit requires a form element');
            }
            if (href === location.href)
                return true;
            opts.url = requestURL(href);
            //console.log(opts.url+"--"+requestURL(location.href));
            if (opts.url === requestURL(location.href))
                return true;
            event.preventDefault();
            opts = $.extend(true, opts, {
                url: href,
                type: $this.attr("method") || "GET",
                data: $this.serializeArray(),
                element: this
            });
            // 发起请求
            $.pjax.request(opts);
        });
    };
    $.pjax.xhr = null;
    $.pjax.opts = {};
    $.pjax.state = {};
    // 默认选项
    $.pjax.defaults = {
        element: null,
        push: true, // true is push, false is replace, null for do nothing
        show: '', // 展示的动画
        title: '', // 标题
        suffix: '', // 标题后缀
        prefix: '', //标题前缀
        //ajax:
        timeout: 2000,
        type: 'GET',
        dataType: 'html',
        data: {
            pjax: true
        },
        beforeSend: function(xhr) {
            $($.pjax.opts.container).trigger('pjax.start', [xhr, $.pjax.opts]);
            xhr && xhr.setRequestHeader('X-PJAX', true);
        },
        complete: function(xhr) {
            $($.pjax.opts.container).trigger('pjax.end', [xhr, $.pjax.opts]);
        },
        error: function() {
            $.pjax.opts.callback && $.pjax.opts.callback.call($.pjax.opts.element, {
                type: 'error'
            });
            location.href = $.pjax.opts.url;
        }
    };
    // 发送请求
    $.pjax.request = function(opts) {
        opts = $.extend(true, $.pjax.defaults, opts);
        if (opts.pop === null || !opts.pop)
            opts.container = $(opts.element).attr('rel') || opts.container;
        $.pjax.opts = opts;
        $.pjax.opts.success = $.pjax.success;

        if ($.pjax.xhr && $.pjax.xhr.readyState < 4) {
            $.pjax.xhr.onreadystatechange = $.noop;
            $.pjax.xhr.abort();
        }
        $.pjax.xhr = $.ajax($.pjax.opts);
    };
    // 调用成功后的回调函数
    $.pjax.success = function(data) {
        var opts = $.pjax.opts;
        if ((data || '').indexOf('<html') !== -1) {
            opts.error();
            return false;
        }
        var title = opts.title, el;
        if (!title) {
            var matches = data.match(/<title>(.*?)<\/title>/);
            if (matches)
                title = matches[1];
            if (!title && opts.element) {
                el = $(opts.element);
                title = el.attr('title') || el.text();
            }
        }
        if (title) {
            if (!title.startsWith(opts.prefix))
                title = opts.prefix + title;
            if (!title.endsWith(opts.suffix))
                title += opts.suffix;
            document.title = title;
        }
        $.pjax.state = {
            container: opts.container,
            timeout: opts.timeout,
            show: opts.show,
            title: title,
            url: opts._url
        };
        var query = $.param(opts.data);
        if (query !== "") {
            $.pjax.state.url = opts.url + (/\?/.test(opts.url) ? "&" : "?") + query;
        }
        if (opts.push) {
            if (!$.pjax.active) {
                history.replaceState($.extend({}, $.pjax.state, {
                    url: null
                }), document.title);
                $.pjax.active = true;
            }
            history.pushState($.pjax.state, document.title, opts._url);
        } else if (opts.push === false) {
            history.replaceState($.pjax.state, document.title, opts._url);
        }

        // 展现函数
        if (!opts.showFn) {
            opts.showFn = function(data, fn) {
                $.pjax.showFn(opts.show, opts.container, data, fn, opts.initUI);
            };
        }
        opts.showFn && opts.showFn(data, function() {
            opts.callback && opts.callback.call(opts.element, {
                type: 'ok'
            });
        });
    };
    // 展现函数
    $.pjax.showFn = function(showType, container, data, fn, initUI) {
        var fx = null;
        if (typeof showType === 'function') {
            fx = showType;
        } else {
            if (!(showType in $.pjax.showFx))
                showType = "_default";
            fx = $.pjax.showFx[showType];
        }
        fx && fx.call($(container), data, function() {
            var hash = location.hash;
            if (hash !== '') {
                location.href = hash;
                //for FF
                if (/Firefox/.test(navigator.userAget)) {
                    history.replaceState($.extend({}, $.pjax.state, {
                        url: null
                    }), document.title);
                }
            } else {
                window.scrollTo(0, 0);
            }
            fn && fn.call(this, data);
        }, initUI);
    };
    // 展现动画
    $.pjax.showFx = {
        "_default": function(data, callback, initUI) {
            this.html(data);
            callback && callback.call(this, data);
            initUI && initUI(this);
        },
        fade: function(data, callback, initUI) {
            var _this = this;
            this.fadeOut(500, function() {
                _this.html(data).fadeIn(500, function() {
                    callback && callback.call(_this, data);
                    initUI && initUI(_this);
                });
            });
        }
    };
    // 浏览器前进、后退事件
    var popped = ('state' in window.history), initialURL = location.href;
    $(window).bind('popstate', function(event) {
        var initialPop = !popped && location.href === initialURL;
        popped = true;
        if (initialPop)
            return;
        var state = event.state;
        if (state && state.container) {
            if ($(state.container).length) {
                var data = {
                    title: state.title,
                    url: state.url || location.href,
                    container: state.container,
                    push: null,
                    timeout: state.timeout,
                    pop: true
                };
                $.pjax.request(data);
            } else {
                window.location = location.href;
            }
        }
    });

    $.support.pjax = window.history && window.history.pushState && window.history.replaceState &&
            // pushState isn't reliable on iOS until 5.
            !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/);
    // 判断浏览器是否支持该插件
    if (!$.support.pjax) {
        $.pjax = function() {
            return true;
        };
        $.pjax.request = function(opts) {
            if (opts && opts.url) {
                location.href = opts.url;
            }
        };
    }
    //添加state属性到jQuery对象
    if ($.inArray('state', $.event.props) < 0)
        $.event.props.push('state');

    function requestURL(url) {
        return (url || '').replace(/\#.*?$/, '').replace('?pjax=true', '').replace('&pjax=true', '');
    }
})(jQuery);